/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package provider.impl.genomemap.data;

import commons.provider.ProviderException;
import commons.provider.impl.BaseXMLFileBasedProviderImpl;
import genomemap.data.TAB08Data;
import genomemap.model.TAB08;
import genomemap.provider.Providers;
import org.dom4j.Element;

import java.util.*;

/**
 * @author Susanta Tewari
 * @since Jul 30, 2011
 */
public class TAB08DataProviderImplXML extends BaseXMLFileBasedProviderImpl<TAB08Data>
        implements Providers.TAB08DataProviderFileBased {

    /**
     * Method description
     *
     *
     * @param root
     *
     * @return
     *
     * @throws ProviderException
     */
    @Override
    protected TAB08Data toValue(final Element root) throws ProviderException {


        // 4. get 'offspring' elements from the root
        @SuppressWarnings("unchecked") final List<Element> offsprings = root.elements("offspring");


        // 5. count the # of sites
        final int genesCount = offsprings.get(0).elementText("genotype").length();


        // 6. prepare data for GtArrayK69Data
        // 6.2 holder of offspring sequence for each offspring label
        final Map<String, String> labelledGenotype = new HashMap<>();
        final Map<String, Integer> labelledFreq    = new HashMap<>();


        // 7. get elements 'label', 'sequence' and 'frequency' from the 'allele' element
        for (final Element offspring : offsprings) {

            final String offspring_label = offspring.attributeValue("label");
            final String genotype        = offspring.elementText("genotype");
            final int frequency          = Integer.parseInt(offspring.elementText("frequency"));


            // 5.1 check the invariant: all sequences have the same number of sites
            if (genotype.length() != genesCount) {

                throw new ProviderException("genes count does not match for offspring "
                                            + offspring_label);
            }

            labelledGenotype.put(offspring_label, genotype);
            labelledFreq.put(offspring_label, frequency);
        }


        // 6.4 check for element 'genes' in the root element
        // 6.4.1.1 get the mutation labels
        final List<String> geneLabels = Arrays.asList(root.elementText("genes").trim().split("-"));


        // exchange prob
        final List<Double> exchangeProbs = new ArrayList<>();

        for (final String prob : root.elementText("exchangeProb").trim().split("-")) {

            try {

                final double val = Double.parseDouble(prob);

                exchangeProbs.add(val);

            } catch (NumberFormatException ex) {
                throw new ProviderException("error parsing exchange probability", ex);
            }
        }


        // check the invariance on the number of genes and the number of exchange probs
        if (geneLabels.size() - 1 != exchangeProbs.size()) {

            throw new ProviderException(
                "invariance geneLabels.size() != exchangeProbs.size()-1 failed");
        }


        // 6.4.1.2 check the invariant: # of mutation labels == sites count
        if (geneLabels.size() != genesCount) {
            throw new ProviderException("Number of gene labels does not match the number of genes");
        }


        // create the data
        try {

            return new TAB08Data(new TAB08(geneLabels, exchangeProbs), labelledGenotype,
                                 labelledFreq);

        } catch (IllegalArgumentException e) {
            throw new ProviderException("Data integrity error", e);
        }
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public Class<TAB08> getModelClass() {
        return TAB08.class;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public Class<TAB08Data> getDataClass() {
        return TAB08Data.class;
    }
}
